/**
 * File Comment: add everything!!! :D
 */
package edu.gatech.oad.project.coordinator;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.FileHandler;
import java.util.logging.Filter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import edu.gatech.oad.project.externalcommunication.IOConverter;
import edu.gatech.oad.project.networking.serverSessions.loginSessionServer;
import edu.gatech.oad.project.operations.data.PatientInformation;
import edu.gatech.oad.project.operations.scheduling.Schedule;
import edu.gatech.oad.project.operations.users.Patient;
//import edu.gatech.oad.project.operations.users.User;
import edu.gatech.oad.project.operations.users.UserCache;

/**
 * This class coordinates between the GUIs
 * and the operations layer to bridge
 * the gap between front end and back end.
 * 
 * @author Group 17
 * @version 1
 */
public class Office { 

    /**
     * Field myLogger: holds the logget for the office file
     */
    private static final Logger MYLOGGER = Logger.getLogger("edu.gatech." +
    		"oad.project.coordinator");
    
    /**
     * Field Filehandler: stores the file handler for the entire project
     */
    public static FileHandler FILEHANDLER;
    
    /**
     * Field users: all the users in the system
     */
    public static final UserCache USERS;
    
    /**
     * Field scheduler: all the appointments stored by date
     */
    public static final Schedule SCHEDULER;
    
    /**
     * Field IOConverter: connection to the database
     */
    public static final IOConverter CONVERTER; 
    
    /**
     * Field tokens: haspmap of security tokens based on client socket
     */
    public static final Map<Socket, Long> TOKENS;
    
    /**
     * Field pool: pool of threads
     */
    public static final ExecutorService POOL;
    
    /**
     * Field rand: used to generate security token
     */
    public static Random RAND;
    
    static {
        FILEHANDLER = null;
        try {
            FILEHANDLER = new FileHandler("application.log");
        } catch (SecurityException e) {
            MYLOGGER.log(Level.SEVERE, "Security Exception " +
            		"creating the logger file handler", e);
            e.printStackTrace();
        } catch (IOException e) {
            MYLOGGER.log(Level.SEVERE, "IO Exception creating " +
            		"the logger file handler", e);
            e.printStackTrace();
        }
        
        FILEHANDLER.setFilter( new Filter() {
            public boolean isLoggable(LogRecord record) {
                //we would put whatever logic we need in here to 
                //determine if this should be logged.
                //for demo purposes, I am putting true
                return true;
            }
        });
        MYLOGGER.addHandler(FILEHANDLER);
        MYLOGGER.setLevel(Level.ALL);
        
        CONVERTER = new IOConverter();
        SCHEDULER = new Schedule(CONVERTER);
        USERS = new UserCache();
        TOKENS = new HashMap<Socket, Long>();
        POOL = Executors.newCachedThreadPool();
        RAND = new Random();

        MYLOGGER.finest("office created");
    }

    /**
     * Main server side method that waits for incomming calls from
     * clients and adds them to the thread pool. 
     * 
     * @param args
     */
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        final int port = 1337;  // $codepro.audit.disable numericLiterals
        try {
            serverSocket = new ServerSocket(port);
        } catch (IOException e) {
            MYLOGGER.severe("Could not listen on port: " + Integer.toString(port) + e);
            System.exit(1);
        }

        final boolean cont = true;
        while (cont) { // $codepro.audit.disable potentialInfiniteLoop, constantConditionalExpression
            Socket clientSocket = null;
            try {
                clientSocket = serverSocket.accept(); // $codepro.audit.disable nullPointerDereference
            } catch (IOException e) {
                MYLOGGER.severe("Accept failed." + e);
         
            }
            loginSessionServer session = new loginSessionServer(clientSocket);
            POOL.execute(session);
        }

    }
    
    /**
     * String of office
     * 
     * @return the string
     */
    @Override
    public String toString() {
        return "The Doctor's Office Software for CS 2340.";
    }
    
    /**
     * @param name of the patient
     * @param id of the patient
     * @param info new information
     */
    public void addPatient(String name, int id, PatientInformation info){
        final Patient pat = new Patient(name, id, SCHEDULER, info);
        MYLOGGER.info("Attempts to add  a new patient");
        USERS.addPatient(pat);
    }
    
    /**
     * If new info is empty, we assume the only
     * changes are the new name and the new id.
     * 
     * @param name the old name of the patient
     * @param id the old id of the patient
     * @param newName of the patient
     * @param newID of the patient
     * @param nInfo of the patient
     */
    public void updatePatient(String name, int id, String newName, 
            int newID, PatientInformation nInfo) {
        PatientInformation newInfo;
        if (nInfo == null){
            newInfo = new PatientInformation(); 
        }
        else {
            newInfo = nInfo;
        }
        newInfo.setName(newName);
        
        final Patient pat = USERS.getPatient(id);
        USERS.removePatient(pat);
        
        pat.setName(newName);
        pat.setID(newID);
        pat.updateInformation(newInfo);
        
        USERS.addPatient(pat);
        
    }

    /**
     * removes a patient
     * 
     * @param name the name of the patient
     * @param id the id of the patient
     */
    public void removePatient(String name, int id) {
        final Patient temp = new Patient(name, id);
        USERS.removePatient(temp);
        
        
    }

    /**
     * Get a patient by its id
     * 
     * @param id of the patient
     * 
     * @return the patient
     */
    public Patient getPatient(int id) {
        
        return USERS.getPatient(id);
    }
    
    /**
    * Verifies the user is has a valid username and password.
    * The method returns a list of strings that consist of several
    * key pieces of data: the first string will be "true" or "false"
    * depending on if the login verification was successful. If it
    * was succesful, the second value is a string of the id and
    * the third value is a string of the rank (patient, nurse, 
    * doctor, or sys admin). If unsuccessful, the second value is
    * the number of current attempts if aplicable (-1 if not) and
    * and the third value is a string value of the type of error.
    * attempt = too many attempts
    * password = invalid password
    * inactive = user is pending aceptance from admin
    * username = invalid username
    * 
    * 
    * @param username the username
    * @param password the password
    * 
    * @return information about the attempt
    */
    public String[] loginToSystem(String username, String password){
        final String[] fin;
        //User user;
        fin = CONVERTER.login(username, password);
        
        return fin;
    }
}
